import java.util.ArrayList;
import java.util.Iterator;


public class cell {
	
	
	@SuppressWarnings("unchecked")
	private ArrayList _listeners = new ArrayList();
	@SuppressWarnings("unchecked")
	public synchronized void addEventListener(ICellEventListener l) { _listeners.add(l); }
	public synchronized void removeEventListener(ICellEventListener l) {	_listeners.remove(l); }
	@SuppressWarnings("unchecked")
	private synchronized void fireEventCellBitmaskChanged() {
		
		Iterator i = _listeners.iterator();
		while (i.hasNext()) {
			((ICellEventListener) i.next()).handleEventCellBitmaskChanged(this);
		}
	}
	@SuppressWarnings( "unchecked" )
	private synchronized void fireEventCellValueChanged() {
		
		Iterator i = _listeners.iterator();
		while (i.hasNext()) {
			((ICellEventListener) i.next()).handleEventCellValueChanged(this);
		}
	}
	
	/* hold the possible values in a bitmask:
	 * 	for possibility n (1 through 9) bitmask = 2^(n-1)
	 * 		1 : 2^0 = 000000001 = 1
	 * 		2 : 2^1 = 000000010 = 2
	 * 		3 : 2^2 = 000000100 = 4
	 * 		4 : 2^3 = 000001000 = 8
	 * 		5 : 2^4 = 000010000 = 16
	 * 		6 : 2^5 = 000100000 = 32
	 * 		7 : 2^6 = 001000000 = 64
	 * 		8 : 2^7 = 010000000 = 128
	 * 		9 : 2^8 = 100000000 = 256
	 * 
	 *	a cell with all values possible is the sum of these values: 511
	 *	a cell with only 1 possible value is an even power of 2
	 *  a cell with multiple possible values is the sum of those values (i.e. 1 and 3 = 1+4 = 5)
	 * 
	 */
	private int _bitmask = 511;
	private int _value = 0;
	
    //default constructor has bitmask value of 511, cell value of 0
    public cell() { _bitmask = 511; _value = 0; }
    
    //parameterized constructor will set the value
    public cell(int i) { if ( 0 <= i && i <= 9) { setValue(i); } }
    
    public int  getBitmask() { return _bitmask; }
    
    public void setBitmask(int i) {
    	if (!(_bitmask == i || i == 0)) {
    		_bitmask = (1 <= i && i <= 511) ? i : 511;
    		fireEventCellBitmaskChanged();
    		//value check
    	    /* The integer value (1-9) of the cell if known.  This will resolve correctly if only 1 bit is set in the bitmask.
    	     * find the exponent of base 2 from the cell's bitmask:
    		 * 	example:  2^3=8 --> log2(8)=3
    		 * 			
    		 * 		for bitmask value y, find x (the exponent)
    		 * 			  2^x=y --> log2(y)=x
    		 * 
    		 * 		using the rule:  the log of y with an exponent of x is x times the log of y
    		 * 		log(y^x)=x*log(y)
    		 * 		
    		 * 		solving for x:  
    		 * 		x = (log(y^x)) / log(y)
    		 * 
    		 * 		for bitmask value 8 (which is equivalent to 2^3)
    		 * 		3 = (log(8)) / log(2)
    		 * 
    		 * 		this means that we can treat a bitmask value of 8 as a cell value of 3 (only the 3rd bit is set)
    		 * 		when the solution is a whole number, we know we have an exponent of 2 in the bitmask, and therefore
    		 * 		a single bit set in the bitmask.
    		 * 
    		 * 		if this value is not a whole number, then we do not have a whole number exponent of 2, meaning we
    		 * 		have more than 1 bit set in the bitmask.
    		 */
    		double x = Math.log(_bitmask) / Math.log(2);
    		int v = (x == Math.round(x)) ? (int)x + 1 : 0;

    		setValue(v);

    	}
    }
    
    public int getValue() { return _value; }
    
    public void setValue(int v) {
    	if (!(_value == v)) {
    		
    		_value = v;
    		fireEventCellValueChanged();
    		
    		//bitmask check
    		int y = (int)Math.pow(2, v-1);
    		if (_bitmask != y || y != 0) { setBitmask(y); }

    	}
    }
    	
    public void reset() { setBitmask(511); setValue(0); }
    
    /* toggle a bit using bitwise exclusive OR (results in 1 if both bits are different)
     * bits are numbered 1-9, representing the possible cell values
     *   000000101 (original value)
     *   000010000 (bit to toggle, #5 or 2^(5-1) or 2^4)
     *   ---------
     *   000010101 (result)
     */
    public void toggleBit(int i) { setBitmask(_bitmask ^ (int)Math.pow(2, i-1)); }
    
    /* Set a bit to 1 using bitwise inclusive OR (results in 1 if either bit is 1)
     * bits are numbered 1-9, representing the possible cell values
     *   000000101 (original value)
     *   000010000 (bit to toggle, #5 or 2^(5-1) or 2^4)
     *   ---------
     *   000010101 (result)
     */
    public void setBit(int i) { setBitmask(_bitmask | (int)Math.pow(2, i-1)); }
    
    /* Clear a bit to 0 using bitwise NOT (invert all bits and then AND the results together (1 if both bits are 1)
     * bits are numbered 1-9, representing the possible cell values
     *   000000101 (original value)
     *   111101111 (bit to clear, #5 or 2^(5-1) or 2^4)... inverted before AND operation
     *   ---------
     *   000000101 (result)
     */
    public void clearBit(int i) { setBitmask(_bitmask & ~(int)Math.pow(2, i-1)); }
    
    /* To check if a bit is set, AND the bitmask with the desired value.  if the result equals the desired value, then true
     * bits are numbered 1-9, representing the possible cell values
     *   000010101 (original value)
     *   000010000 (bit to check, #5 or 2^(5-1) or 2^4)
     *   ---------
     *   000010000 (result equals the check value, so this is true)
     */
    public boolean isBitSet(int i) {
        return ((_bitmask & (int)Math.pow(2, i-1)) == (int)Math.pow(2, i-1) ) ? true : false;
    }
    
    
    // return the number of bits set 
    // this algorithm is known as 'HAMMING WEIGHT' : http://en.wikipedia.org/wiki/Hamming_weight
    
    public int getBitCount()
    {
//    	int i = _value;
//        i = i - ((i >> 1) & 0x55555555);
//        i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
//        return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
    	
    	//this algorithm is actually implemented natively by java
    	return Integer.bitCount(_value);
    }
    
    
    
    
    public String toString() {
        String t = "";
        for (int i = 1; i < 10; i++) { t += (isBitSet(i)) ? i : "."; }
        return t;
    }
	
}
